package com.lhz.framework.aspect.http;

import cn.hutool.json.JSONUtil;
import com.lhz.common.utils.UUIDUtils;
import com.lhz.project.system.mapper.base.LogMapper;
import com.lhz.project.system.model.entity.base.HttpRecordLog;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @Author: LiHuaZhi
 * @Date: 2021/9/4App 22:34
 * @Description:
 **/
@Component
@Aspect
@Slf4j
public class HttpLogAspect {

    @Resource
    private LogMapper logMapper;

    @Pointcut("@annotation(com.lhz.framework.aspect.http.HttpLog)")
    public void httpLog() {
    }

    @Before(value = "httpLog()")
    public void paramValid(JoinPoint point) {
        // 开始时间
        HttpLogContext.setTime(System.currentTimeMillis());
        Object[] paramObj = point.getArgs();
        if (paramObj.length > 0) {
            HttpLog httpLog = getAnnotationLog(point);
            HttpRecordLog recordLog = new HttpRecordLog();
            String id = UUIDUtils.getUuId();
            String url = paramObj[httpLog.urlIndex()] == null ? null : paramObj[httpLog.urlIndex()].toString();
            String requestParams = paramObj[httpLog.reqParamIndex()] == null ? null : paramObj[httpLog.reqParamIndex()].toString();
            String httpMethod = paramObj[httpLog.reqParamIndex()] == null ? httpLog.reqTypeStr() : paramObj[httpLog.reqParamIndex()].toString();
            log.info("开始请求[{}]===》》请求URL：{}，请求参数：{}，请求方式：{}", id, url, requestParams, httpMethod);

            recordLog.setId(id);
            recordLog.setUrl(url);
            recordLog.setRequestParams(requestParams);
            recordLog.setHttpMethod(httpMethod);
            HttpLogContext.setLog(recordLog);
        }
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(value = "httpLog()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        handleLog(joinPoint, null, jsonResult);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "httpLog()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }

    private void handleLog(final JoinPoint joinPoint, final Exception e, Object result) {
        long time = System.currentTimeMillis() - HttpLogContext.getTime();
        HttpRecordLog recordLog = HttpLogContext.getLog();
        String url = recordLog.getUrl();
        String requestParams = recordLog.getRequestParams();
        String httpMethod = recordLog.getHttpMethod();
        String id = recordLog.getId();

        String errorTrace = null;
        String errorMsg = null;
        String responseStr = null;
        if (e != null) {
            // 设置日志信息
            String trace = ExceptionUtils.getStackTrace(e);
            errorTrace = trace.length() > 4000 ? trace.substring(0, 4000) : trace;
            errorMsg = e.getMessage();
            log.info("请求异常结束[{}]===》》请求URL：{}，请求参数：{}，请求方式：{}，错误信息：{}，请求耗时：{} ms", id, url, requestParams, httpMethod, errorMsg, time);
            log.info("请求异常结束[{}]===》》异常堆栈：{}", id, errorTrace);
        } else {
            if (result != null) {
                responseStr = JSONUtil.toJsonStr(result);
            }
            log.info("请求正常结束[{}]===》》请求URL：{}，请求参数：{}，请求方式：{}，响应参数：{}，请求耗时：{} ms", id, url, requestParams, httpMethod, responseStr, time);
        }
        recordLog.setTime(time);
        recordLog.setResponseParams(responseStr);
        recordLog.setErrorMsg(errorMsg);
        recordLog.setErrorTrace(errorTrace);
        // 写入数据库
        logMapper.insertHttpLog(recordLog);

        HttpLogContext.remove();
    }

    /**
     * 是否存在注解，如果存在就获取
     */
    private HttpLog getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(HttpLog.class);
        }
        return null;
    }
}
